// 用户信息表
const mongoose = require("./mgdb.js");
const crypto = require('crypto');
const promisify = require("util").promisify;
const {
	DataRls
} = require("../config/class.js");
// 联表查询对象
const {
	GroupChat
} = require('./groupchatsdb.js'); // 引入GroupChat模型   
const {
	Friend,
	createFriendList,
	addToFriendList,
	getFriendsById,
} = require('./friendsdb.js'); // 引入friendsdb模型   
const {
	Email,
	createEmailList
} = require('./emailsdb.js'); // 引入email模型   
const {
	error
} = require("console");
const {
	onlineUsers,
	isUserOnline
} = require("./onlineUsers.js");

// 用户信息的相关规则
const userSchema = new mongoose.Schema({
	nickname: {
		type: String,
		required: [true, '用户未填时，昵称需要随机生成'],
		trim: true,
		minlength: [3, '昵称长度下限为3个字符'],
		maxlength: [10, '昵称长度上限为10个字符']
	},
	username: {
		type: String,
		required: [true, '账户名称是必填项'],
		trim: true,
		unique: true,
		minlength: [6, '账户名称长度下限为6个字符'],
		maxlength: [15, '账户名称长度上限为15个字符'],
	},
	password: {
		type: String,
		required: [true, '密码是必填项'],
		minlength: [8, '密码的最小长度为8位'],
		// maxlength: [18, '密码长度上限为18个字符'],
		// 实际应用中，密码字段还需要进行加密处理    
	},
	avatarPath: {
		type: String,
		default: "/headimg/default-1.jpeg"
		// 根据需要可以添加对路径格式的验证规则    
	},
	bio: {
		type: String,
		trim: true,
		maxlength: [30, '个人介绍最大长度为30'],
		default: "这个用户还没有介绍自己哦!"
		// 简介字段的长度限制已经具体给出    
	},
	points: { //积分
		type: Number,
		default: 0,
		min: [0, '游戏积分不能为负']
	},
	groups: [{
		type: mongoose.Schema.Types.ObjectId,
		ref: 'GroupChat'
	}],
	friendId: {
		type: mongoose.Schema.Types.ObjectId,
		ref: 'Friend'
	},
	emailId: {
		type: mongoose.Schema.Types.ObjectId,
		ref: 'Email'
	}
});

// 绑定规则和mongodb数据库
const User = mongoose.model('User', userSchema);

//MD5加密
function md5Crypto(str) {
	let md5 = crypto.createHash('md5');
	md5.update(str);
	return md5.digest('hex');
}

// 用户登录行为
async function login(body) {
	// 获取登录请求参数
	const {
		username,
		password
	} = body;
	// 向数据库发送查询
	try {
		// 查询用户是否存在
		// 异步查询所有的房间列表
		let loginUser = await User.findOne({
			username,
			// 因为注册的时候用了MH5加密，所以登录使用mongodb找值的时候，也需要加密
			password: md5Crypto(password),
			// 这里populate是联表查询的方法
			// }).populate('groups').populate('friendId').populate('emailId').exec();
		})
		// 使用Result类封装返回结果
		// 这个对象是我们自己封装的对象类型，用途是用来封装数据的返回值
		return new DataRls(
			loginUser ? "登录成功" : "登录失败",
			loginUser ? loginUser : "请检查账户和密码是否正确", // 返回的用户信息    
			Boolean(loginUser) // 登录结果    
		);
	} catch (error) {
		// 捕获并处理错误
		console.log("数据库错误", error);
		return new DataRls("登录中发生错误", null, false, error.message); // 返回错误信息    
	}
}

// 用户注册行为
async function register(body) {
	// 解构获取注册请求参数
	const {
		nickname, //昵称
		username, //账户（唯一）
		password, //密码
		avatarPath, //头像路径
		bio, //签名
	} = body;

	try {
		// 查询用户是否存在
		let findUser = await User.findOne({
			username
		});

		if (findUser == null) {
			// 获取注册结果
			// 这里假设用户的输入已经经过服务器和客户端的验证
			let UserRls = await User.create({
				nickname,
				username,
				password: md5Crypto(password),
				avatarPath,
				bio,
				groups: new Array(),
			})

			// 好友列表
			let FriendRls = await createFriendList(UserRls._id);
			// 邮件列表
			// 为什么需要有邮件?因为用户不是总是在线的
			// 当用户离线时,受到的消息,就以邮件的形式保存
			let EmailRls = await createEmailList(UserRls._id);

			// 直接赋值好友列表ID,邮件列表ID  
			UserRls.friendId = FriendRls.data._id;
			UserRls.emailId = EmailRls.data._id
			// 保存更新后的文档
			await UserRls.save();

			return new DataRls(
				(UserRls && FriendRls.result) ? "注册成功" : "注册失败",
				UserRls, // 返回的用户信息    
				Boolean(UserRls) // 登录结果    
			);
		} else {
			throw new DataRls(
				"注册失败，账户重复",
				null,
				false
			);
		}
	} catch (error) {
		// 捕获并处理错误    
		console.log("数据库错误", error);
		throw new DataRls("注册中发生错误", null, false, error.message);
	}

}


// 根据id获取指定好友列表的所有状态，如是否在线，头像，昵称等
async function getFriendMessage(findID) {
	// 通过id找到好友列表
	let findRlt = await getFriendsById(findID);
	// 判断好友列表是否存在
	try {
		if (!findRlt.result) {
			throw new DataRls(
				"好友列表不存在",
				"获取用户好友列表失败",
				false,
				new Error("好友列表不存在"),
			);
		} else if (findRlt.data.friends.length == 0) {
			return new DataRls(
				"好友列表长度为0",
				[],
				true
			);
		} else {
			// 好友列表存在，开始处理
			// 由于Mongoose为了维护数据的一致性和响应式变化跟踪，采用了深度的不可变数据模型
			// 它的数据都设为了只读的，我们现在需要往这个对象中增加值，需要使用值的副本修改
			// 所以如果想让值变得可以修改，需要使用深度拷贝的方法拷贝这个值
			let friends = JSON.parse(JSON.stringify(findRlt.data.friends));

			// 循环处理好友列表
			// 这里不能使用forEach循环，因为forEach不会等待异步操作完成就继续下一次循环。
			// 这可能会导致数据不一致或者竞态条件等问题
			// 异步函数本来就是为了能够像使用同步函数一样，去使用异步函数，forEech不会等待异步函数函数执行完成再执行下一步
			// 除了这个以外，forEach不能够主动跳出循环，break，return无法跳出
			// 如果forEach想跳出循环，需要使用抛出错误的方式 try catch
			for (let item of friends) {
				let {
					friendId
				} = item;

				// 获取好友是否在线
				item.state = isUserOnline(friendId) ? "在线" : "离线";

				// 获取好友的相关信息
				// 返回时，用mongodb的select方法，把重要信息筛选出去
				let friendMessage = await User.findById(item.friendId)
					.select("-password -emailId -friendId -groups -_id");
				// 将获取到的指定字段保存到列表中
				item.message = JSON.parse(JSON.stringify(friendMessage));
				console.log(item);
			}


			return new DataRls(
				"好友列表处理成功",
				friends,
				true
			);
		}
	} catch (error) {
		console.log(error)
		return new DataRls(
			"好友列表处理失败",
			[],
			false,
			error
		);
	}
}

// 根据用户的查找需求，查找对应的好友
async function findUserMessage(findInput) {
	try {
		let findRls = await User.find({
			"$or": [{
				"nickname": {
					// 模糊搜索
					"$regex": findInput,
					// 不区分大小写
					"$options": "i"
				}
			},
			{
				"username": {
					"$regex": findInput,
					"$options": "i"
				}
			}
			]
		}).select("-password -emailId -friendId -groups");

		return new DataRls(
			"搜索请求成功",
			findRls,
			true
		);
	} catch (error) {
		return new DataRls(
			"搜索请求失败",
			[],
			false,
			error
		);
	}
}

// 根据
async function findUsername(username) {
	let findUser = await User.findOne({
		username
	});
	
	// 根据结果返回布尔值
	if (findUser) {
		return false;
	}else{
		return true;
	}
}




// 导出数据库模块
module.exports = {
	login, //登录模块
	register, //注册模块
	User, //规则对象
	getFriendMessage, //获取指定用户的好友状态
	findUserMessage, //根据条件查找所有的好友
	findUsername
}